﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SlimDX;
using SlimDX.DXGI;
using SlimDX.Direct3D11;

namespace Apparat.Picking
{
    public class Picker
    {
        public static void Pick(int x, int y)
        {
            Vector3 near = new Vector3(x, y, 0);
            Vector3 far = new Vector3(x, y, 1);

            Viewport vp = DeviceManager.Instance.viewport;
            Matrix wvp = CameraManager.Instance.currentCamera.ViewPerspective;

            Vector3 nearU = Vector3.Unproject(near, vp.X, vp.Y, vp.Width, vp.Height, vp.MinZ, vp.MaxZ, wvp);
            Vector3 farU = Vector3.Unproject(far, vp.X, vp.Y, vp.Width, vp.Height, vp.MinZ, vp.MaxZ, wvp);

            Vector3 dir = farU - nearU;
            Ray ray = new Ray(nearU, dir);
            float distance = float.MaxValue;

            foreach (Renderables.Renderable item in Scene.Instance.RenderObjects)
            {
                if (item is Renderables.Pickable)
                {
                    ((Renderables.Pickable)item).Intersects(ray, out distance);
                }
            }
        }

        public struct ObjectAndDistance
        {
            public Renderables.Pickable Object;
            public float distance;
        }

        public static List<ObjectAndDistance> Pick(int x, int y, List<Renderables.Renderable> RenderObjects)
        {
            List<ObjectAndDistance> pickedObjects = new List<ObjectAndDistance>();

            Vector3 near = new Vector3(x, y, 0);
            Vector3 far = new Vector3(x, y, 1);

            Viewport vp = DeviceManager.Instance.viewport;
            Matrix wvp = CameraManager.Instance.currentCamera.ViewPerspective;

            Vector3 nearU = Vector3.Unproject(near, vp.X, vp.Y, vp.Width, vp.Height, vp.MinZ, vp.MaxZ, wvp);
            Vector3 farU = Vector3.Unproject(far, vp.X, vp.Y, vp.Width, vp.Height, vp.MinZ, vp.MaxZ, wvp);

            Vector3 dir = farU - nearU;
            Ray ray = new Ray(nearU, dir);
            float distance = float.MaxValue;

            foreach (Renderables.Renderable item in RenderObjects)
            {
                if (item is Renderables.Pickable)
                {
                    if (((Renderables.Pickable)item).Intersects(ray, out distance))
                    {
                        ObjectAndDistance pickedObject;
                        pickedObject.Object = (Renderables.Pickable)item;
                        pickedObject.distance = distance;
                        pickedObjects.Add(pickedObject);
                    }
                }
            }
            return pickedObjects;
        }

        public static ObjectAndDistance PickFirst(int x, int y, List<Renderables.Renderable> renderObjects)
        {
            Vector3 near = new Vector3(x, y, 0);
            Vector3 far = new Vector3(x, y, 1);

            Viewport vp = DeviceManager.Instance.viewport;
            Matrix wvp = CameraManager.Instance.currentCamera.ViewPerspective;

            Vector3 nearU = Vector3.Unproject(near, vp.X, vp.Y, vp.Width, vp.Height, vp.MinZ, vp.MaxZ, wvp);
            Vector3 farU = Vector3.Unproject(far, vp.X, vp.Y, vp.Width, vp.Height, vp.MinZ, vp.MaxZ, wvp);

            Vector3 dir = farU - nearU;
            Ray ray = new Ray(nearU, dir);

            foreach (Renderables.Renderable item in renderObjects)
            {
                if (item is Renderables.Pickable)
                {
                    float distance = float.MaxValue;
                    if (((Renderables.Pickable)item).Intersects(ray, out distance))
                    {
                        ObjectAndDistance pickedObject;
                        pickedObject.Object = (Renderables.Pickable)item;
                        pickedObject.distance = distance;
                        return (pickedObject);
                    }
                }
            }

            return new ObjectAndDistance();
        }

        public static ObjectAndDistance Pick(int x, int y, Renderables.Renderable Object)
        {
            ObjectAndDistance PickedObject = new ObjectAndDistance();

            Vector3 near = new Vector3(x, y, 0);
            Vector3 far = new Vector3(x, y, 1);

            Viewport vp = DeviceManager.Instance.viewport;
            Matrix wvp = CameraManager.Instance.currentCamera.ViewPerspective;

            Vector3 nearU = Vector3.Unproject(near, vp.X, vp.Y, vp.Width, vp.Height, vp.MinZ, vp.MaxZ, wvp);
            Vector3 farU = Vector3.Unproject(far, vp.X, vp.Y, vp.Width, vp.Height, vp.MinZ, vp.MaxZ, wvp);

            Vector3 dir = farU - nearU;
            Ray ray = new Ray(nearU, dir);
            float distance = float.MaxValue;


            if (Object is Renderables.Pickable)
            {
                if (((Renderables.Pickable)Object).Intersects(ray, out distance))
                {
                    PickedObject.Object = (Renderables.Pickable)Object;
                    PickedObject.distance = distance;
                }
            }

            return PickedObject;
        }

        public static bool PickInXZPlane(int x, int y, out Vector3 point)
        {
            Vector3 near = new Vector3(x, y, 0);
            Vector3 far = new Vector3(x, y, 1);

            Viewport vp = DeviceManager.Instance.viewport;
            Matrix wvp = CameraManager.Instance.currentCamera.ViewPerspective;

            Vector3 nearU = Vector3.Unproject(near, vp.X, vp.Y, vp.Width, vp.Height, vp.MinZ, vp.MaxZ, wvp);
            Vector3 farU = Vector3.Unproject(far, vp.X, vp.Y, vp.Width, vp.Height, vp.MinZ, vp.MaxZ, wvp);

            Vector3 dir = farU - nearU;
            Ray ray = new Ray(nearU, dir);

            Plane plane = new Plane(new Vector3(0, 0, 0), new Vector3(0, 1, 0));

            float dist;

            if (Ray.Intersects(ray, plane, out dist))
            {
                dir.Normalize();
                Vector3 pos = nearU + Vector3.Multiply(dir, dist);
                //Renderables.R2D.Circle circle = new Renderables.R2D.Circle(0.05f, 32, pos.X, pos.Z);
                //Scene.Instance.AddRenderObject(circle);
                point = pos;
                return true;
            }
            point = Vector3.Zero;
            return false;
        }

        public static bool PickInPlane(int x, int y, Plane plane, out Vector3 point)
        {
            point = Vector3.Zero;

            Vector3 near = new Vector3(x, y, 0);
            Vector3 far = new Vector3(x, y, 1);

            Viewport vp = DeviceManager.Instance.viewport;
            Matrix wvp = CameraManager.Instance.currentCamera.ViewPerspective;

            Vector3 nearU = Vector3.Unproject(near, vp.X, vp.Y, vp.Width, vp.Height, vp.MinZ, vp.MaxZ, wvp);
            Vector3 farU = Vector3.Unproject(far, vp.X, vp.Y, vp.Width, vp.Height, vp.MinZ, vp.MaxZ, wvp);

            Vector3 dir = farU - nearU;
            Ray ray = new Ray(nearU, dir);

            float dist;

            if (Ray.Intersects(ray, plane, out dist))
            {
                dir.Normalize();
                Vector3 pos = nearU + Vector3.Multiply(dir, dist);
                point = pos;
                return true;
            }

            return false;
        }
    }
}
